package com.byron.media.server;

import com.byron.media.server.client.MediaClient;
import com.byron.media.server.config.MediaServerConfig;
import com.byron.media.server.handlers.*;
import com.byron.media.server.sessions.RealSession;
import com.byron.media.server.utils.IniReader;
import com.byron.media.server.client.MediaClientConfig;
import com.byron.media.server.utils.NetworkUtils;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.stream.ChunkedWriteHandler;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 流媒体服务器主入口
 * 用netty开启监听
 */
@Slf4j
public class MediaServer extends Thread {

    public static final int MTU = 1024;

    private ServerBootstrap bootstrap;

    private ChannelFuture closeFuture;

    private List<MediaClient> mediaClients;    // 作为代理客户端连接到上层主服务器

    private MediaServerConfig config;

    private NetworkUtils networkUtils;

    private EventLoopGroup workerGroup;

    private EventLoopGroup bossGroup;

    private EventLoopGroup clientGroup;

    public MediaServer(MediaServerConfig config) {
        this.config = config;
        this.networkUtils = new NetworkUtils();

        int processors = 0;
        if(config.getProcess() == 0){
            processors = Runtime.getRuntime().availableProcessors();
        } else {
            processors = config.getProcess();
        }
        this.bossGroup = new NioEventLoopGroup();
        this.workerGroup = new NioEventLoopGroup(processors * 2);
        this.clientGroup = new NioEventLoopGroup();
        this.mediaClients = new ArrayList<>();
    }

    @Override
    public void run() {
        if(!"singleton".equals(config.getServerMode())){
            new Thread(() -> connectToMainServer()).start();
        }
        startAsServer();
//        networkUtils.destroy();
    }

    /**
     * 连接到主服务器
     */
    private void connectToMainServer() {

        List<String> serverHosts = config.getServerHosts();
        mediaClients = new ArrayList<>();

        // 连接上所有的终端
        for(String host :serverHosts){
            String[] temp = host.split(":");
            String address = temp[0];
            int port = temp.length > 1 ? Integer.parseInt(temp[1]) : 2553;

            MediaClientConfig clientConfig = new MediaClientConfig();
            clientConfig.setServerPort(port);
            clientConfig.setServerAddress(address);
            clientConfig.setServerId(this.config.getServerId());
            MediaClient mediaClient = new MediaClient(clientGroup, clientConfig);
            mediaClient.start();
            mediaClients.add(mediaClient);
        }


//        sessionContainer.setMediaClient(mediaClient);
    }

    /**
     * 作为服务器启动
     */
    private void startAsServer(){

        try {
            bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .option(ChannelOption.SO_RCVBUF, 500 * 1024 * 1024)
                    .option(ChannelOption.SO_SNDBUF, 500 * 1024 * 1024)
                    .option(ChannelOption.MAX_MESSAGES_PER_READ, 100)

                    // 内存池优化
                    .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)

                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new ChunkedWriteHandler());
                            p.addLast(new MediaDecoder());
                            p.addLast(new MediaEncoder());
                            if(config.getHeart() > 0){
                                p.addLast(new MediaHeartBeat(config.getHeart()));
                            }
                            p.addLast(new RealSession());
                            p.addLast(new MediaServerHandler(config));
                        }
                    });
//                    .childOption(ChannelOption.AUTO_READ, true);

            // Start the server.
            closeFuture = bootstrap.bind(config.getPort()).sync();

            log.warn("Gonsin Media Server 配置启动完成");

            // Wait until the server socket is closed.
            closeFuture.channel().closeFuture().sync();
            log.warn("Gonsin Media Server 关闭");

//            log.info("EchoServer.main end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }


    public void end(){
        closeFuture.channel().pipeline().fireChannelInactive();
    }

//    /**
//     * TODO 发送到组
//     */
//    public void sendToGroup(String group, byte channel, byte type, long seq, byte[] data, int length){
//        List<MediaData> mediaData = dataFactory.createData(channel, seq, data, length, type);
//        mediaData.forEach(d -> {
//            sessionContainer.sendToOther(null, group, d);
//        });
//    }

    public static void main(String[] args) throws IOException {

        boolean rtsp = false;

        // 读取配置文件
        MediaServerConfig config = MediaServerConfig.getInstance();

        String file = null;
        if(args != null && args.length > 0){
            for(int i = 0; i < args.length; i++) {
                if("-file".equals(args[i])){
                    i++;
                    file = args[i];
                }
                if("-rtsp".equals(args[i])){
                    rtsp = true;
                }
            }
        }
        if(file == null){
            file =  "./media_server.ini";
        }
        File iniFile = new File(file);
        if(iniFile.exists()){
            IniReader reader = new IniReader(iniFile.getAbsolutePath());
            String portStr = reader.getValueStr("server", "port");
            if(portStr != null && !"".equals(portStr)){
                config.setPort(Integer.parseInt(portStr));
            }

            String heartStr = reader.getValueStr("server", "heart");
            if(heartStr != null && !"".equals(heartStr)){
                config.setHeart(Integer.parseInt(heartStr));
            }

            String serverIdStr = reader.getValueStr("server", "server_id");
            if(serverIdStr != null && !"".equals(serverIdStr)){
                config.setServerId(Integer.valueOf(serverIdStr).byteValue());
            }

            String processesStr = reader.getValueStr("server", "processes");
            if(processesStr != null && !"".equals(processesStr)){
                config.setProcess(Integer.parseInt(processesStr));
            }


            String hosts = reader.getValueStr("server", "hosts");
            if(hosts != null && !"".equals(hosts)){
                config.setServerHosts(Arrays.asList(hosts.split(",")));
            }

            String rootPath = reader.getValueStr("server", "root_path");
            if(rootPath != null && !"".equals(rootPath)){
                config.setRootFilePath(rootPath);
            }

            Boolean log = reader.getValue("server", "log");
            if(log != null){
                config.setLog(log);
            }

            String maxMillsStr = reader.getValueStr("codec", "max_mills");
            if(maxMillsStr != null && !"".equals(maxMillsStr)){
                config.setMaxMills(Integer.parseInt(maxMillsStr));
            }

            String minMillsStr = reader.getValueStr("codec", "min_mills");
            if(minMillsStr != null && !"".equals(minMillsStr)){
                config.setMinMills(Integer.parseInt(minMillsStr));
            }

            String mainServer = reader.getValueStr("rtsp", "main_server");
            if(minMillsStr != null && !"".equals(minMillsStr)){
                config.setMainServer(mainServer);
            }

            String mainPortStr = reader.getValueStr("rtsp", "main_port");
            if(minMillsStr != null && !"".equals(minMillsStr)){
                config.setMainPort(Integer.parseInt(mainPortStr));
            }
        }

        new MediaServer(config).start();
//        if(rtsp ){
//            new RtspMediaClient(config).start();
//        } else {
//        }
    }

//    /**
//     * 该服务器是否为代理服务器
//     */
//    public boolean isProxyServer() {
//        return config.isProxyServer();
//    }
}
